/*
 * Copyright 2013 JROSE
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jrose.core.util;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;
import org.springframework.util.Assert;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.List;
import java.util.Random;

/**
 * Random utilities.
 *
 * @author Xiaohai Zhang
 * @since 2013-08-18 17:48
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
abstract public class RandomUtils {
    private static final String NUMBERIC = "0123456789";
    private static final String ALPHABET_NUMBERIC = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    @Getter private static final Random random;

    static {
        random = initializeRandom();
    }

    private static Random initializeRandom() {
        try {
            SecureRandom ran = SecureRandom.getInstance("SHA1PRNG");
            byte[] seed = ran.generateSeed(20);
            ran = new SecureRandom(seed);
            return ran;
        } catch (NoSuchAlgorithmException ignored) {
            // ignored
            return new Random();
        }
    }

    public static String nextObjectId() {
        return new ObjectId().toString();
    }

    public static int nextInt() {
        return random.nextInt();
    }

    public static int nextInt(int n) {
        return random.nextInt(n);
    }

    public static int nextInt(int start, int end) {
        if (start > end) {
            throw new IllegalArgumentException("start greater than end");
        }
        if (start == end) {
            return start;
        }
        int i = end - start + 1;
        return nextInt(i) + start;
    }

    public static String randomNumeric(int len) {
        Assert.isTrue(len >= 0);
        return randomString(len, NUMBERIC);
    }

    public static String randomAlphabetNumeric(int len) {
        Assert.isTrue(len >= 0);
        return randomString(len, ALPHABET_NUMBERIC);
    }

    public static boolean hitProbability(double probability) {
        int base = 10000;
        int range = (int) (probability * base);
        int i = nextInt(0, base - 1);
        return i < range;
    }

    public static boolean hitPercentage(int percentage) {
        int i = nextInt(1, 100);
        return i <= percentage;
    }

    public static <T> T randomListElement(List<T> list) {
        if (list == null || list.isEmpty()) {
            return null;
        }
        int index = nextInt(list.size());
        return list.get(index);
    }

    // private methods:

    private static String randomString(int len, CharSequence source) {
        StringBuilder sbuf = new StringBuilder();
        for (int i = 0; i < len; i++) {
            sbuf.append(source.charAt(nextInt(source.length())));
        }
        return sbuf.toString();
    }
}
